Semantic UI library
Two tiny files.

teff styles plain, accessible HTML and sprinkles behavior on top with vanilla web components. No framework, no build step, no dependencies — drop in a stylesheet and go. Inspired by Oat CSS.

~9 kB CSS ~3 kB JS 0 dependencies WCAG AA contrast dark mode built in
<link rel="stylesheet" href="https://unpkg.com/@thinking.tools/teff/teff.min.css">
<script src="https://unpkg.com/@thinking.tools/teff/teff.min.js"></script>

<!-- or -->  npm install @thinking.tools/teff

* with a little extra flavor — like the grain it's named after.

Theme & tokens

Every color resolves through light-dark(), so dark mode is automatic. Customize by overriding a handful of custom properties — or flip the presets in the top bar: accent, radius, density, theme.

--primary
--secondary
--muted
--background
--foreground
--border
--danger
--success
--warning
--logo-blue
--logo-citron
--logo-sky
<html data-accent="blue" data-radius="soft" data-density="compact">

/* or override tokens directly */
:root {
  --primary: light-dark(#2068c9, #7ab3ff);
  --radius-button: 0.75rem;
}

Typography

Prose needs no class names — semantic HTML is styled out of the box.

Heading level 3

An hgroup pairs it with a subtitle.

Body text with a link, bold, italic, inline code, highlight and small print.

Press Cmd + K to search. Deleted and inserted text, no longer accurate, one mispelled word, an HTML abbreviation, H2O, e=mc2 and program output.

The best class name is the one you never had to write.
  • Unordered item one
  • Unordered item two
Version
1.0.0
License
MIT
Dependencies
None — two files, vanilla everything.
The teff logo ring
Nine grains, one ring — the teff mark.

Buttons

A plain <button> is the primary action. Variants via data-variant, styles via .outline/.ghost, sizes via .small/.large/.icon. Icons just work; wrap long labels in .truncate.

Link button
  • Badges

    Status labels: a .badge span with the same data-variant tones as buttons; .outline keeps it quiet.

    Default Secondary Success Warning Danger Outline

    Avatars

    Initials in a figure data-variant="avatar" — size with .small/.large, nest them to stack a team.

    AB
    CD
    EF
    JK
    LM
    NO

    Cards & grid

    A 12-column .row/.col-* grid that stacks on mobile, plus a one-liner .autogrid that fits as many cards as space allows.

    Card one

    Half width via .col-6.

    Card two

    Cards are shadow-ringed surfaces.

    auto
    auto
    auto

    Forms

    Native inputs, selects, checkboxes, radios, switches, ranges, file and search fields — styled directly, validation states included. Field hints and errors hang off data-field. With the JS bundle on the page, password fields grow a show/hide toggle — opt out with data-static.

    Too short — minimum 8 characters.
    https://

    Tables

    Wrap a plain <table> in .table for horizontal scrolling on small screens. Numbers are tabular by default; <caption> and <tfoot> are styled too.

    Bundle breakdown
    Component Type Size
    Tabs Custom element 0.9 kB
    Toast JS API 1.1 kB
    Card CSS only 0.1 kB
    Total 2.1 kB

    Accordion

    Native <details> — adjacent ones fuse into a group.

    What is teff?

    An ultra-lightweight, semantic component library — one CSS file and one JS file, each usable on its own.

    Does it need a build step?

    No. Link the CSS and JS and write plain semantic HTML.

    Tabs

    <teff-tabs> wires up ARIA and roving keyboard navigation around your existing markup.

    Overview panel — arrow keys move between tabs.

    Features panel — ARIA wiring is automatic.

    Pricing panel — roving tabindex for keyboard nav.

    Dialog

    Native <dialog> opened declaratively with command/commandfor. Clicking the backdrop closes it — opt out with data-static.

    Delete project?

    This action cannot be undone.

    The project and all of its data will be permanently removed.

    Alerts

    A role="alert" div — neutral by default, tinted by data-variant.

    A new version is available — refresh whenever you're ready.
    Your changes were saved successfully.
    Your subscription expires soon.
    Something went wrong — please retry.

    Progress & meter

    Native <progress> and <meter> — a bare progress is indeterminate, and meters self-color by their distance from optimum.

    85% 20%

    Spinner & skeleton

    Set aria-busy="true" on anything to get a spinner; add data-spinner="overlay" to dim and disable its contents. The skeleton shimmer runs entirely on the compositor.

    Tooltips

    A plain title attribute is progressively enhanced into a styled tooltip.

    Toasts

    One imperative call: teff.toast(message, title, options).

    Motion

    Opt-in, compositor-friendly animations — only opacity, translate and filter ever move, and everything respects prefers-reduced-motion. data-enter fades an element in; data-enter="stagger" cascades its children 100 ms apart.

    Staggered enter

    Each child fades, lifts and unblurs in turn.

    last one

    A quick head-shake for rejected input: call teff.shake(el) when validation fails, or toggle data-shake yourself.

    Utilities

    A small set of helpers for the gaps between components: stacks, spacing, truncation, .sr-only, shadows and radii.

    hstack muted text truncated-very-long-token-name ms-auto